#
# Copyright 2005,2007,2012 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# GNU Radio is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNU Radio; see the file COPYING.  If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#

from gnuradio import gr, filter
import math
import cmath

#
#  An analog deemphasis filter:
#
#           R
#  o------/\/\/\/---+----o
#                   |
#                   = C
#                   |
#                  ---
#
#  Has this transfer function:
#
#             1            1
#            ----         ---
#             RC          tau
#  H(s) = ---------- = ----------
#               1            1
#          s + ----     s + ---
#               RC          tau
#
#  And has its -3 dB response, due to the pole, at
#
#  |H(j w_c)|^2 = 1/2  =>  s = j w_c = j (1/(RC))
#
#  Historically, this corner frequency of analog audio deemphasis filters
#  been specified by the RC time constant used, called tau.
#  So w_c = 1/tau.
#
#  FWIW, for standard tau values, some standard analog components would be:
#  tau = 75 us = (50K)(1.5 nF) = (50 ohms)(1.5 uF)
#  tau = 50 us = (50K)(1.0 nF) = (50 ohms)(1.0 uF)
#
#  In specifying tau for this digital deemphasis filter, tau specifies
#  the *digital* corner frequency, w_c, desired.
#
#  The digital deemphasis filter design below, uses the
#  "bilinear transformation" method of designing digital filters:
#
#  1. Convert digitial specifications into the analog domain, by prewarping
#     digital frequency specifications into analog frequencies.
#
#     w_a = (2/T)tan(wT/2)
#
#  2. Use an analog filter design technique to design the filter.
#
#  3. Use the bilinear transformation to convert the analog filter design to a
#     digital filter design.
#
#     H(z) = H(s)|
#                 s = (2/T)(1-z^-1)/(1+z^-1)
#
#
#         w_ca         1          1 - (-1) z^-1
#  H(z) = ---- * ----------- * -----------------------
#         2 fs        -w_ca             -w_ca
#                 1 - -----         1 + -----
#                      2 fs              2 fs
#                               1 - ----------- z^-1
#                                       -w_ca
#                                   1 - -----
#                                        2 fs
#
#  We use this design technique, because it is an easy way to obtain a filter
#  design with the -6 dB/octave roll-off required of the deemphasis filter.
#
#  Jackson, Leland B., _Digital_Filters_and_Signal_Processing_Second_Edition_,
#    Kluwer Academic Publishers, 1989, pp 201-212
#
#  Orfanidis, Sophocles J., _Introduction_to_Signal_Processing_, Prentice Hall,
#    1996, pp 573-583
#


class fm_deemph(gr.hier_block2):
    """
    FM Deemphasis IIR filter.
    """

    def __init__(self, fs, tau=75e-6):
        """

        Args:
            fs: sampling frequency in Hz (float)
            tau: Time constant in seconds (75us in US, 50us in EUR) (float)
        """
        gr.hier_block2.__init__(self, "fm_deemph",
                                gr.io_signature(1, 1, gr.sizeof_float),  # Input signature
                                gr.io_signature(1, 1, gr.sizeof_float))  # Output signature

        # Digital corner frequency
        w_c = 1.0 / tau

        # Prewarped analog corner frequency
        w_ca = 2.0 * fs * math.tan(w_c / (2.0 * fs))

        # Resulting digital pole, zero, and gain term from the bilinear
        # transformation of H(s) = w_ca / (s + w_ca) to
        # H(z) = b0 (1 - z1 z^-1)/(1 - p1 z^-1)
        k = -w_ca / (2.0 * fs)
        z1 = -1.0
        p1 = (1.0 + k) / (1.0 - k)
        b0 = -k / (1.0 - k)

        btaps = [ b0 * 1.0, b0 * -z1 ]
        ataps = [      1.0,      -p1 ]

        # Since H(s = 0) = 1.0, then H(z = 1) = 1.0 and has 0 dB gain at DC

        if 0:
            print "btaps =", btaps
            print "ataps =", ataps
            global plot1
            plot1 = gru.gnuplot_freqz(gru.freqz(btaps, ataps), fs, True)

        deemph = filter.iir_filter_ffd(btaps, ataps, False)
        self.connect(self, deemph, self)

#
#  An analog preemphasis filter, that flattens out again at the high end:
#
#               C
#         +-----||------+
#         |             |
#  o------+             +-----+--------o
#         |      R1     |     |
#         +----/\/\/\/--+     \
#                             /
#                             \ R2
#                             /
#                             \
#                             |
#  o--------------------------+--------o
#
#  (This fine ASCII rendition is based on Figure 5-15
#   in "Digital and Analog Communication Systems", Leon W. Couch II)
#
#  Has this transfer function:
#
#                   1
#              s + ---
#                  R1C
#  H(s) = ------------------
#               1       R1
#          s + --- (1 + --)
#              R1C      R2
#
#
#  It has a corner due to the numerator, where the rise starts, at
#
#  |Hn(j w_cl)|^2 = 2*|Hn(0)|^2  =>  s = j w_cl = j (1/(R1C))
#
#  It has a corner due to the denominator, where it levels off again, at
#
#  |Hn(j w_ch)|^2 = 1/2*|Hd(0)|^2  =>  s = j w_ch = j (1/(R1C) * (1 + R1/R2))
#
#  Historically, the corner frequency of analog audio preemphasis filters
#  been specified by the R1C time constant used, called tau.
#
#  So
#  w_cl = 1/tau  =         1/R1C; f_cl = 1/(2*pi*tau)  =         1/(2*pi*R1*C)
#  w_ch = 1/tau2 = (1+R1/R2)/R1C; f_ch = 1/(2*pi*tau2) = (1+R1/R2)/(2*pi*R1*C)
#
#  and note f_ch = f_cl * (1 + R1/R2).
#
#  For broadcast FM audio, tau is 75us in the United States and 50us in Europe.
#  f_ch should be higher than our digital audio bandwidth.
#
#  The Bode plot looks like this:
#
#
#                     /----------------
#                    /
#                   /  <-- slope = 20dB/decade
#                  /
#    -------------/
#               f_cl  f_ch
#
#  In specifying tau for this digital preemphasis filter, tau specifies
#  the *digital* corner frequency, w_cl, desired.
#
#  The digital preemphasis filter design below, uses the
#  "bilinear transformation" method of designing digital filters:
#
#  1. Convert digitial specifications into the analog domain, by prewarping
#     digital frequency specifications into analog frequencies.
#
#     w_a = (2/T)tan(wT/2)
#
#  2. Use an analog filter design technique to design the filter.
#
#  3. Use the bilinear transformation to convert the analog filter design to a
#     digital filter design.
#
#     H(z) = H(s)|
#                 s = (2/T)(1-z^-1)/(1+z^-1)
#
#
#                                  -w_cla
#                              1 + ------
#                                   2 fs
#                         1 - ------------ z^-1
#              -w_cla              -w_cla
#          1 - ------          1 - ------
#               2 fs                2 fs
#  H(z) = ------------ * -----------------------
#              -w_cha              -w_cha
#          1 - ------          1 + ------
#               2 fs                2 fs
#                         1 - ------------ z^-1
#                                  -w_cha
#                              1 - ------
#                                   2 fs
#
#  We use this design technique, because it is an easy way to obtain a filter
#  design with the 6 dB/octave rise required of the premphasis filter.
#
#  Jackson, Leland B., _Digital_Filters_and_Signal_Processing_Second_Edition_,
#    Kluwer Academic Publishers, 1989, pp 201-212
#
#  Orfanidis, Sophocles J., _Introduction_to_Signal_Processing_, Prentice Hall,
#    1996, pp 573-583
#


class fm_preemph(gr.hier_block2):
    """
    FM Preemphasis IIR filter.
    """
    def __init__(self, fs, tau=75e-6, fh=-1.0):
        """

        Args:
            fs: sampling frequency in Hz (float)
            tau: Time constant in seconds (75us in US, 50us in EUR) (float)
            fh: High frequency at which to flatten out (< 0 means default of 0.925*fs/2.0) (float)
        """
        gr.hier_block2.__init__(self, "fm_preemph",
                                gr.io_signature(1, 1, gr.sizeof_float),  # Input signature
                                gr.io_signature(1, 1, gr.sizeof_float))  # Output signature

	# Set fh to something sensible, if needed.
	# N.B. fh == fs/2.0 or fh == 0.0 results in a pole on the unit circle
	# at z = -1.0 or z = 1.0 respectively.  That makes the filter unstable
	# and useless.
	if fh <= 0.0 or fh >= fs/2.0:
		fh = 0.925 * fs/2.0

	# Digital corner frequencies
	w_cl = 1.0 / tau
	w_ch = 2.0 * math.pi * fh

	# Prewarped analog corner frequencies
	w_cla = 2.0 * fs * math.tan(w_cl / (2.0 * fs))
	w_cha = 2.0 * fs * math.tan(w_ch / (2.0 * fs))

	# Resulting digital pole, zero, and gain term from the bilinear
	# transformation of H(s) = (s + w_cla) / (s + w_cha) to
	# H(z) = b0 (1 - z1 z^-1)/(1 - p1 z^-1)
	kl = -w_cla / (2.0 * fs)
	kh = -w_cha / (2.0 * fs)
	z1 = (1.0 + kl) / (1.0 - kl)
	p1 = (1.0 + kh) / (1.0 - kh)
	b0 = (1.0 - kl) / (1.0 - kh)

	# Since H(s = infinity) = 1.0, then H(z = -1) = 1.0 and
	# this filter  has 0 dB gain at fs/2.0.
	# That isn't what users are going to expect, so adjust with a
	# gain, g, so that H(z = 1) = 1.0 for 0 dB gain at DC.
	w_0dB = 2.0 * math.pi * 0.0
	g =        abs(1.0 - p1 * cmath.rect(1.0, -w_0dB))  \
	   / (b0 * abs(1.0 - z1 * cmath.rect(1.0, -w_0dB)))

	btaps = [ g * b0 * 1.0, g * b0 * -z1 ]
	ataps = [          1.0,          -p1 ]

        if 0:
            print "btaps =", btaps
            print "ataps =", ataps
            global plot2
            plot2 = gru.gnuplot_freqz(gru.freqz(btaps, ataps), fs, True)

        preemph = filter.iir_filter_ffd(btaps, ataps, False)
        self.connect(self, preemph, self)
